﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.DirectoryServices;
using System.Linq;
using System.Reflection;
using System.Web.Security;
using Microsoft.International.Converters.PinYinConverter;

namespace RedRiver.Framework.Web.Extensions{
    /// <summary>
    /// 目录服务 功能扩展
    /// </summary>
    public static class DirectoryExtensions{
        private const string OUSchemaName = "organizationalUnit";
        private const string PropertyNameUserAccountControl = "userAccountControl";


        private static bool CheckObjectClass(this DirectoryEntry entry, string className){
            return entry.SchemaClassName.Equals(className, StringComparison.CurrentCultureIgnoreCase);
        }

        private static bool IsUserEntry(this DirectoryEntry entry){
            return entry.SchemaClassName.Equals("user", StringComparison.CurrentCultureIgnoreCase);
        }

        public static DirectoryEntry GetMembershipEntry(this ActiveDirectoryMembershipProvider provider){
            if (provider == null){
                throw new ArgumentNullException("provider");
            }
            FieldInfo adConnField = provider.GetType().GetField("adConnectionString",
                                                                BindingFlags.Instance | BindingFlags.NonPublic |
                                                                BindingFlags.GetField);
            if (adConnField != null){
                string adConnectionString = adConnField.GetValue(provider).ToString();
                return new DirectoryEntry(adConnectionString);
            }
            return null;
        }

        /// <summary>
        /// 获取指定名称的OU对象
        /// </summary>
        /// <param name="root"></param>
        /// <param name="ouName"></param>
        /// <returns></returns>
        public static DirectoryEntry GetChildOU(this DirectoryEntry root, string ouName){
            if (null == root){
                throw new ArgumentNullException("root");
            }
            if (string.IsNullOrEmpty(ouName)){
                throw new ArgumentNullException("ouName");
            }
            if (!ouName.StartsWith("ou=", StringComparison.CurrentCultureIgnoreCase)){
                ouName = "ou=" + ouName;
            }
            var searcher =
                new DirectorySearcher(root, "(&(" + ouName + ")(objectClass=organizationalUnit))");
            SearchResult result = searcher.FindOne();
            if (result != null){
                return result.GetDirectoryEntry();
            } else{
                //建立 组织
                DirectoryEntry ou = root.Children.Add(ouName, OUSchemaName);
                ou.CommitChanges();
                return ou;
            }
        }

        #region DisableUser

        public static void DisableUser(this DirectoryEntry userEntry){
            if (userEntry == null){
                throw new ArgumentNullException("userEntry");
            }
            var val = (ADS_USER_FLAG_ENUM) userEntry.Properties[propertyName: PropertyNameUserAccountControl].Value;
            userEntry.Properties[PropertyNameUserAccountControl].Value = val | ADS_USER_FLAG_ENUM.ADS_UF_ACCOUNTDISABLE;
            //ADS_UF_ACCOUNTDISABLE;
        }

        public static void DisableUser(this SearchResult userSearchResult){
            userSearchResult.GetDirectoryEntry().DisableUser();
        }

        public static void DisableUser(this DirectorySearcher userSearcher){
            foreach (SearchResult result in  userSearcher.FindAll()){
                result.GetDirectoryEntry();
            }
        }

        #endregion DisableUser

        #region Membership

        /// <summary>
        /// 停用AD账号
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public static string DisableADUser(string userName){
            IEnumerable<ActiveDirectoryMembershipProvider> providers =
                Membership.Providers.OfType<ActiveDirectoryMembershipProvider>();
            if (providers.Any()){
                ActiveDirectoryMembershipProvider adProvider = providers.First();
                DirectoryEntry ou = GetMembershipEntry(adProvider);
                //DirectoryEntry user = ou.Children.Find("cn=" + userName); //在用户对象创建zixian用户
                try{
                    var searcher =
                        new DirectorySearcher(ou, "(&(CN=" + userName + ")(objectCategory=person)(objectClass=user))");
                    SearchResult result = searcher.FindOne();
                    if (result != null){
                        DirectoryEntry userEntry = result.GetDirectoryEntry();
                        userEntry.DisableUser();
                        string userPath = userEntry.Path;
                        //userEntry.Parent.Children.Remove(userEntry);
                        return "停用账户 " + userPath;
                    }
                } catch (Exception ex){
                    return ex.Message;
                }
            }
            return string.Empty;
        }

        #region Pinyin

        private static Dictionary<char, string[]> _charPinyinDict;
        /// <summary>
        /// 汉字字符 拼音字典
        /// </summary>
        private static Dictionary<char, string[]> CharPinyinDict{
            get{
                if (_charPinyinDict == null){
                    _charPinyinDict = new Dictionary<char, string[]>();
                }
                return _charPinyinDict;
            }
        }
        /// <summary>
        /// 获取一个中文字符的拼音
        /// </summary>
        /// <param name="c">一个有效的汉字字符</param>
        /// <returns>返回汉字字符的拼音</returns>
        /// <remarks>如果字符不是一个有效地汉字字符<see cref="ChineseChar.IsValidChar"/>则返回该字符的字符串</remarks>
        public static string[] GetChineseCharPinyin(char c){
            if(ChineseChar.IsValidChar(c)){
                return new string[]{c.ToString()};
            }
            if (CharPinyinDict.ContainsKey(c)){
                return CharPinyinDict[c];
            }
            var cchar = new ChineseChar(c);
            ReadOnlyCollection<string> pinyins = cchar.Pinyins;

            string[] pys;
            if (!cchar.IsPolyphone){
                pys = new[]{FormatPinyin(pinyins[0])};
                CharPinyinDict.Add(c, pys);
                return pys;
            }
            //多音字数量
            var pyList = new List<string>();
            for (int i = 0; i < pinyins.Count; i++){
                if (pinyins[i] == null){
                    break;
                }
                string py = FormatPinyin(pinyins[i]);
                if (!pyList.Contains(py)){
                    //过滤掉不同声调的多音字
                    pyList.Add(py);
                }
            }
            pys = pyList.ToArray();
            CharPinyinDict.Add(c, pys);
            return pys;
        }

        /// <summary>
        /// 格式化拼音形式如 "Zhang"
        /// </summary>
        /// <param name="pinyin"></param>
        /// <returns></returns>
        private static string FormatPinyin(string pinyin){
            var chars = new char[pinyin.Length];
            int count = 0;
            for (int i = 0; i < pinyin.Length; i++){
                char c = pinyin[i];
                if (char.IsLetter(c)){
                    if (i == 0){
                        chars[i] = (char.ToUpper(c));
                    } else{
                        chars[i] = (char.ToLower(c));
                    }
                    count++;
                } else{
                    break;
                }
            }
            return new string(chars, 0, count);
        }

        /// <summary>
        /// 获取全部拼音多音字组合
        /// </summary>
        /// <param name="fullName">中文姓名</param>
        /// <returns>姓名的拼音多音字组合</returns>
        public static string[] GetChinesePinyin(string fullName){
            var pinyinList = new List<string>();
            for (int p = 0; p < fullName.Length; p++){
                string[] pinyin = GetChineseCharPinyin(fullName[p]);
                if (pinyinList.Count == 0){
                    pinyinList.AddRange(pinyin);
                } else{
                    if (pinyin.Length == 1){
                        for (int l = 0; l < pinyinList.Count; l++){
                            pinyinList[l] += pinyin[0];
                        }
                    } else{
                        int len = pinyinList.Count;
                        for (int i = 1; i < pinyin.Length; i++){
                            pinyinList.AddRange(pinyinList.ToArray());
                        }
                        for (int i = 0; i < pinyin.Length; i++){
                            for (int l = 0; l < len; l++){
                                pinyinList[len*i + l] += pinyin[i];
                            }
                        }
                    }
                }
            }

            return pinyinList.ToArray();
        }

        #endregion Pinyin

        #endregion Membership
    }

    /// <summary>
    /// 用户登录验证结果
    /// </summary>
    public enum LoginResult{
        /// <summary>
        /// 正常登录
        /// </summary>
        LOGIN_USER_OK = 0,

        /// <summary>
        /// 用户不存在
        /// </summary>
        LOGIN_USER_DOESNT_EXIST,

        /// <summary>
        /// 用户帐号被禁用
        /// </summary>
        LOGIN_USER_ACCOUNT_INACTIVE,

        /// <summary>
        /// 用户密码不正确
        /// </summary>
        LOGIN_USER_PASSWORD_INCORRECT
    }

    /// <summary>
    /// 用户属性定义标志
    /// </summary>
    [Flags]
    public enum ADS_USER_FLAG_ENUM{
        /// <summary>
        /// 登录脚本标志。如果通过 ADSI LDAP 进行读或写操作时，该标志失效。如果通过 ADSI WINNT，该标志为只读。
        /// </summary>
        ADS_UF_SCRIPT = 0X0001,

        /// <summary>
        /// 用户帐号禁用标志
        /// </summary>
        ADS_UF_ACCOUNTDISABLE = 0X0002,

        ///
        /// <summary>
        /// 主文件夹标志
        /// </summary>
        ADS_UF_HOMEDIR_REQUIRED = 0X0008,

        /// <summary>
        /// 过期标志
        /// </summary>
        ADS_UF_LOCKOUT = 0X0010,

        /// <summary>
        /// 用户密码不是必须的
        /// </summary>
        ADS_UF_PASSWD_NOTREQD = 0X0020,

        ///
        /// 密码不能更改标志
        ///
        ADS_UF_PASSWD_CANT_CHANGE = 0X0040,

        /// <summary>
        /// 使用可逆的加密保存密码
        /// </summary
        ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED = 0X0080,

        /// <summary>
        /// 本地帐号标志
        /// </summary
        ADS_UF_TEMP_DUPLICATE_ACCOUNT = 0X0100,

        /// <summary>
        /// 普通用户的默认帐号类型
        /// </summary
        ADS_UF_NORMAL_ACCOUNT = 0X0200,

        /// <summary>
        /// 跨域的信任帐号标志
        /// </summary>
        ADS_UF_INTERDOMAIN_TRUST_ACCOUNT = 0X0800,

        /// <summary>
        /// 工作站信任帐号标志
        /// </summary>
        ADS_UF_WORKSTATION_TRUST_ACCOUNT = 0x1000,

        /// <summary>
        /// 服务器信任帐号标志
        /// </summary>
        ADS_UF_SERVER_TRUST_ACCOUNT = 0X2000,

        /// <summary>
        /// 密码永不过期标志
        /// </summary>
        ADS_UF_DONT_EXPIRE_PASSWD = 0X10000,

        /// <summary>
        /// MNS 帐号标志
        /// </summary>
        ADS_UF_MNS_LOGON_ACCOUNT = 0X20000,

        /// <summary>
        /// 交互式登录必须使用智能卡
        /// </summary>
        ADS_UF_SMARTCARD_REQUIRED = 0X40000,

        /// <summary>
        /// 当设置该标志时，服务帐号（用户或计算机帐号）将通过 Kerberos 委托信任
        /// </summary>
        ADS_UF_TRUSTED_FOR_DELEGATION = 0X80000,

        /// <summary>
        /// 当设置该标志时，即使服务帐号是通过 Kerberos 委托信任的，敏感帐号不能被委托
        /// </summary>
        ADS_UF_NOT_DELEGATED = 0X100000,

        /// <summary>
        /// 此帐号需要 DES 加密类型
        /// </summary>
        ADS_UF_USE_DES_KEY_ONLY = 0X200000,

        /// <summary>
        /// 不要进行 Kerberos 预身份验证
        /// </summary>
        ADS_UF_DONT_REQUIRE_PREAUTH = 0X4000000,

        /// <summary>
        /// 用户密码过期标志
        /// </summary>
        ADS_UF_PASSWORD_EXPIRED = 0X800000,

        /// <summary>
        /// 用户帐号可委托标志
        /// </summary>
        ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 0X1000000
    }
}